﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;

namespace SmartMathLibrary.Storage
{
    /// <summary>
    /// Defines the change status of the storage.
    /// </summary>
    public enum StorageChangeStatus
    {
        /// <summary>
        /// Describes that the data were changed.
        /// </summary>
        Changed = 0,
        /// <summary>
        /// Describes that the data were not changed.
        /// </summary>
        Unchanged = 1
    }

    /// <summary>
    /// This class represents a normal point storage. The normal storage checks the point values during the adding process.
    /// The point will only get the permission to add, if its x position value is greater than the greatest x position value 
    /// in the storage.
    /// </summary>
    [Serializable]
    public class PointStorage : AbstractPointStorage, SmartMathLibrary.Storage.IPointStorageAddingPermission
    {
        /// <summary>
        /// The minimum y value of the current storage.
        /// </summary>
        private double minY;

        /// <summary>
        /// The maximum y value of the current storage.
        /// </summary>
        private double maxY;

        /// <summary>
        /// The change status of the storage.
        /// </summary>
        private StorageChangeStatus storageChangeStatus;

        /// <summary>
        /// Initializes a new instance of the <see cref="PointStorage"/> class.
        /// </summary>
        /// <param name="size">The size of the storage.</param>
        public PointStorage(int size)
            : base(size)
        {
            this.minY = Double.MaxValue;
            this.maxY = Double.MinValue;
            this.storageChangeStatus = StorageChangeStatus.Unchanged;
        }

        /// <summary>
        /// Gets or sets the storage change status.
        /// </summary>
        /// <value>The storage change status.</value>
        public StorageChangeStatus StorageChangeStatus
        {
            get { return storageChangeStatus; }
            set { storageChangeStatus = value; }
        }

        /// <summary>
        /// Gets the minimum y value of the storage.
        /// </summary>
        /// <value>The minimum y value of the storage.</value>
        public double MinY
        {
            get { return this.minY; }
        }

        /// <summary>
        /// Gets the maximum y value of the storage.
        /// </summary>
        /// <value>The maximum y value of the storage.</value>
        public double MaxY
        {
            get { return this.maxY; }
        }

        /// <summary>
        /// This method adds a new point to the current storage. If the maximum capacity of the 
        /// storage is exceeded, it will automatically overwrite the smallest point of the storage.
        /// If the x value if the point is smaller than the largest x value in the storage, an 
        /// StoragePointAddingPremissionFailed exception will be thrown.
        /// </summary>
        /// <param name="item">The point item to add.</param>
        public virtual void AddPoint(StoragePoint item)
        {
            if (this.CheckAddingPermission(item) == false)
            {
                throw new StoragePointAddingPremissionFailedException(
                    "It is not allowed to add a point which includes a smaller x value than the last point.");
            }

            if (item.Y < this.minY)
            {
                this.minY = item.Y;
            }
            else if (this.maxY < item.Y)
            {
                this.maxY = item.Y;
            }

            if (this.fillSize < this.cacheSize)
            {
                this.fillSize++;
            }

            this.positionData[cachePointer] = item;
            this.cachePointer = (this.cachePointer + 1) % this.cacheSize;
            this.storageChangeStatus = StorageChangeStatus.Changed;
        }

        /// <summary>
        /// Manipulates the y value at a specified position.
        /// </summary>
        /// <param name="index">The index, which specifics the index for the manipulation.</param>
        /// <param name="y">The new y value.</param>
        public void ManipulateValueAt(int index, double y)
        {
            this.positionData[this.TransformElementIndex(index)].Y = y;

            if (this.maxY < y)
            {
                this.maxY = y;
            }
            else if (y < this.minY)
            {
                this.minY = y;
            }
        }

        /// <summary>
        /// This method checks the permission to add a new point to a storage.
        /// </summary>
        /// <param name="item">The position item, which should be checked.</param>
        /// <returns>
        /// True, if the storage data allows the adding of the point otherwise, false.
        /// </returns>
        public bool CheckAddingPermission(StoragePoint item)
        {
            int checkPosition = this.cachePointer - 1;

            if (checkPosition < 0)
            {
                checkPosition = this.cacheSize - 1;
            }

            if (this.positionData[checkPosition].X > item.X)
            {
                return false;
            }

            return true;
        }

        /// <summary>
        /// Reorganizes the points. During the reorganization process the position data
        /// will be sorted by their x values.
        /// </summary>
        public void ReorganizePoints()
        {
            Array.Sort(this.positionData);
            this.cachePointer = 0;
        }

        /// <summary>
        /// Checks the integrity of the complete storage.
        /// </summary>
        /// <returns>True, if all position data in the storage have their 
        /// correct positions and are sorted by there x values otherwise, false.</returns>
        public bool CheckDataIntegrityComplete()
        {
            for (int i = 1; i < this.cacheSize; i++)
            {
                if (this[i - 1].X > this[i].X)
                {
                    return false;
                }
            }

            return true;
        }

        /// <summary>
        /// Checks the integrity of the storage by partially tests.
        /// </summary>
        /// <param name="interval">The interval to test the integrity.</param>
        /// <returns>True, if the partially checked position data in the storage have their 
        /// correct positions and are sorted by there x values otherwise, false.</returns>
        public bool CheckDataIntegrityPartially(int interval)
        {
            for (int i = 1; i < this.cacheSize; i += interval)
            {
                if (this[i - 1].X > this[i].X)
                {
                    return false;
                }
            }

            return true;
        }
    }
}